home *** CD-ROM | disk | FTP | other *** search
- /*
- * Interface driver for the OH1MQK Dual-DDLC (MC145488) card
- * Copyright 1992 by Matti Aarnio, OH1MQK ( Internet: mea@utu.fi )
- * Permission for non-commercial use is hereby granted provided
- * this notice is retained. For info call +358-21-854717,
- * or preferrably reach via the Internet...
- *
- * Portions of this driver were derived from the HAPN-card driver
- * by Jon Bloom, KE3Z, SCC-card driver by PE1CHL and KY3B, and
- * PI-card driver by Dave Perry, VE3IFB, as well as some code by
- * Phil Karn, KA9Q...
- * (Yeah, I had to look for some model to write this :-) )
- *
- *
- * Affected files:
- * ddlc.c ddlc.h ddlc.doc rawhdlc.h rawhdlc.c ddlcvec.asm (NEW!)
- * commands.h config.h config.c makefile pc.tl internet.tl
- */
-
- #include <time.h>
- #include <stdio.h>
- #include <dos.h>
- #include <bios.h>
- #include "global.h"
- #include "mbuf.h"
- #include "iface.h"
- #include "pktdrvr.h"
- #include "netuser.h"
- #include "ddlc.h"
-
- #ifdef AX25 /* Works also w/o AX.25 ! */
- #include "ax25.h"
- #endif
-
- #include "trace.h"
- #include "pc.h"
-
- #include "session.h"
- #include "lapb.h"
- #include "proc.h"
- #include "ip.h"
- #include "devparam.h"
-
- #ifndef FP_OFF
- #define FP_OFF(fp) ((unsigned)(fp))
- #endif
- #ifndef FP_SEG
- #define FP_SEG(fp) ((unsigned)((unsigned long)(fp) >> 16))
- #endif
- #define BUF_IN_USE 0x8000
- #define CONFIGREG 0x0180 /* Offset to config register */
- #define MEMSTART 0x0200 /* Offset to begin of memory */
- #define DTRREG 0x0100 /* Offset to DTR/SCPENA/.. -register */
-
-
- /*INTERRUPT (far *(ddlcint) __ARGS((int dev)))();*/
- extern void ddlcint __ARGS((int16 dev));
- static int32 ddlc_ctl __ARGS((struct iface *iface,int cmd,int set,int32 val));
- static int ddlc_raw __ARGS((struct iface *iface,struct mbuf *bp));
- static int ddlc_stop __ARGS((struct iface *iface));
- static void ddlc_rts __ARGS((struct ddlcchan *ddch, int16 x));
- /*static void setup_ddlc_rx_dma __ARGS((struct ddlcchan *ddch));
- static void setup_ddlc_tx_dma __ARGS((struct ddlcchan *ddch, char *buffer,
- int length)); */
- static void ddlc_buf_combiner __ARGS((struct ddlcchan *ddch));
- static void ddlc_buf_free __ARGS((const char *buf));
- static char *ddlc_buf_alloc __ARGS((struct ddlcchan *ddch, const int extra));
- static void ddlc_buf_purge __ARGS((struct ddlcchan *ddch));
- static void ddlc_scpcomplete __ARGS((DDLC far *ddev,
- const int channel, const int data_ok));
- static void ddlc_timerirq __ARGS((DDLC far *ddev,
- const int channel, const int data_ok));
- static void tdelay __ARGS((register struct ddlcchan *ddch,
- unsigned int time));
- static int ddlc_init __ARGS((const int boardnum, char far *boardbase,
- const int ivec, unsigned int memsize));
- static void chipint __ARGS((int dev, char far *boardbase, int chip,
- int IrqVal));
- static void ddlc_abort __ARGS((const int IrqVal, const int channel));
- static void ddlc_rxint __ARGS((DDLC far *ddev,
- const int channel, const int data_ok));
- static void ddlc_txint __ARGS((DDLC far *ddev,
- const int channel, const int data_ok));
- static void ddlc_txintdefer __ARGS((struct ddlcchan far * ddch));
- static int cdchk __ARGS((const DDLC *ddev, const int channel));
-
- static struct DDLCTAB Ddlc[DDLCMAX]; /* Device table - one entry per card */
- /* This assumes that DDLCMAX == 2 */
- #if (DDLCMAX == 2) /* Yup, Ddlc_handle[]-array will be missing if you
- change DDLCMAX without proper re-write at necessary
- places for multi-board operation. Alike here...
- Remember to edit DDLCVEC.ASM too! */
- static INTERRUPT (*Ddlc_handle[])() = { /* handler interrupt vector table */
- ddlc0vec,
- ddlc1vec
- };
- #endif
-
- static struct ddlcchan Ddlcchan[DDLCCMAX]; /* channel table - max 4 entries per card */
- static int boardcnt = 0;
-
-
- #define CGETPUTW 1
- #ifdef CGETPUTW
- /* KLUDGE: cgetw(), cputw() are for accessing DDLC chip registers, as
- the DDLC-chip latches address lines at the start of /CS signal,
- and does not pay attention to them while /WR or /RW are going
- down & up. Thus a 16-bit access will fail without following
- cludge. Access to SRAM works all right without this. */
-
- static unsigned int cgetw __ARGS((const void far *addr));
-
- static unsigned int cgetw(addr)
- const void far *addr;
- {
- asm les bx,dword ptr addr ;
- asm mov al,byte ptr es:[bx+0] ;
- asm mov ah,byte ptr es:[bx+MEMSTART]; /* Spend a moment with RAM buffer */
- asm mov ah,byte ptr es:[bx+1] ;
- return _AX;
- }
-
- static void cputw __ARGS((const void far *addr, const unsigned int val));
-
- static void cputw(addr,val)
- const void far *addr; const unsigned int val;
- {
- asm les bx,dword ptr addr ;
- asm mov ax,word ptr val ;
- asm mov byte ptr es:[bx+0],al ;
- asm mov al,byte ptr es:[bx+MEMSTART]; /* Spend a moment with RAM buffer */
- asm mov byte ptr es:[bx+1],ah ;
- }
- #else
- #define cgetw(addr) (*(int16*)addr)
- #define cputw(addr,val) (*(int16*)addr=(int16)val)
- #endif
-
-
- /* Interrupt vector is directed here, ddlc#vec feeds argument for this
- board-level interrupt master dispatcher */
- void
- ddlcint(dev)
- int16 dev;
- {
- char far *Board = Ddlc[dev].boardbase;
- int doagain = 1;
- int Mstr = 0;
-
- while (doagain) {
- doagain = 0;
- if (Ddlc[dev].channelcnt == 2) {
- Mstr = ((DDLC *)Board)[1].MStat.mstr_stat.VecNum;
- if (Mstr != 0) {
- /* Channels 2 & 3 */
- chipint(dev,Board,1,Mstr);
- doagain = 1;
- }
- }
- Mstr = ((DDLC *)Board)[0].MStat.mstr_stat.VecNum;
- if (Mstr != 0) {
- /* Channels 0 & 1 */
- chipint(dev,Board,0,Mstr);
- doagain = 1;
- }
- }
- }
-
-
- /* chipint() Now we know which of the two chips the interrupt came from,
- analyze more deeply. */
- static void
- chipint(dev, boardbase, chip, IrqVal)
- int dev; /* Actually board reference - not channel reference! */
- char far *boardbase;
- int chip;
- int IrqVal;
- {
- int channel = dev * CHANNELS_ON_BOARD +
- chip * 2; /* This one or the next channel */
- DDLC *ddev = (DDLC*)((long)boardbase + chip * sizeof(DDLC));
- switch(IrqVal) {
- case 2: /* SCP Complete -- XX: not used at present */
- ddlc_scpcomplete(ddev,channel,TRUE);
- break;
- case 1: /* Channel 0 TIMER interrupt XX: not used at present */
- ddlc_timerirq(ddev,channel+0,TRUE);
- break;
- case 3: /* Channel 1 TIMER interrupt XX: not used at present */
- ddlc_timerirq(ddev,channel+1,TRUE);
- break;
- case 4: /* Channel 0 bit handler normal receiver */
- ddlc_rxint(ddev,channel+0,TRUE);
- break;
- case 5: /* Channel 0 bit handler normal transmitter */
- ddlc_txint(ddev,channel+0,TRUE);
- break;
- case 6: /* Channel 1 bit handler normal receiver */
- ddlc_rxint(ddev,channel+1,TRUE);
- break;
- case 7: /* Channel 1 bit handler normal transmitter */
- ddlc_txint(ddev,channel+1,TRUE);
- break;
- case 8: /* Channel 0 bit handler fault receiver */
- ddlc_rxint(ddev,channel+0,FALSE);
- break;
- case 9: /* Channel 0 bit handler fault transmitter */
- ddlc_txint(ddev,channel+1,FALSE);
- break;
- case 10: /* Channel 1 bit handler fault receiver */
- ddlc_rxint(ddev,channel+1,FALSE);
- break;
- case 11: /* Channel 1 bit handler fault transmitter */
- ddlc_txint(ddev,channel+1,FALSE);
- break;
- case 12: case 13: case 14: case 15: /* System fault! */
- /* Address errors, etc. If so, can't happen with
- the DDLC-card. */
- ddlc_abort(IrqVal, channel);
- break;
- default: /* 0: No (more) interrupt.. */
- break;
- }
- }
-
-
- static void
- ddlc_scpcomplete(ddev,channel,data_ok) /* XX: not used at present */
- DDLC far *ddev;
- const int channel;
- const int data_ok;
- {
- return;
- }
-
-
- static void
- ddlc_timerirq(ddev,channel,data_ok) /* XX: not used at present */
- DDLC far *ddev;
- const int channel;
- const int data_ok;
- {
- return;
- }
-
-
- /* test for busy channel (CD active)
- * returns TRUE if channel busy
- */
- static int
- cdchk(ddev,channel)
- const DDLC *ddev;
- const int channel;
- {
- return ddev[1 & (channel >> 1)]. \
- Ch_Regs[channel & 1]. \
- RxStat.rx_status.CD;
- }
-
-
- static void
- ddlc_abort(IrqVal, channel)
- const int IrqVal, channel;
- {
- /* Foo! Got some system fault from the devices.
- Flush the buffers, and kill the interface -- ddlc_stop() */
-
- struct ddlcchan *ddch = & Ddlcchan[ channel ];
-
- if (IrqVal & 2) /* Channel 1, not 0, within the chip. */
- ++ddch;
-
- if_detach(ddch->iface);
- ddch->iface = NULL; /* if_detach() does free() to that structure.. */
- }
-
-
- /* Buffer management scheme:
-
- Every channel has TWO INCOMING, and TWO OUTGOING buffers.
-
- Buffers are allocated for MTU-size only. Max MTU for all
- channels at the board is circa 1kB for 16kB board, and 2kB
- for 32kB board. (Assuming all channels use same MTU value..)
- Of course total usage can not exceed free buffer memory.
- Single used channel can utilize whole buffer for itself,
- however: buffer length registers are 12 bits only, thus max
- buffer size is 4kB.
-
- Buffer usage (both xmit and rcv) is a typical dual-buffer
- scheme. Fill/empty a buffer while another is being
- sent/received.
-
- Buffer allocation is done in the attach order, but buffer re-use
- after detach of a channel is possible.
-
- */
-
-
-
- /* Buffer allocation scheme:
- <16bit word> -- bit15: 1-> in use, 0-> free
- bits0..14: size of buffer in bytes
- (When 0, no other buffer follows, this is the last)
- <size bytes> -- buffer
-
- When a buffer is allocated, its size is stored in the header word,
- and a pointer is returned to the actual buffer.
-
- Buffer freeing clears bit15, and starts to combine free buffers
- into one larger - to ease fragmentation. If free buffer is last,
- it is blown out alltogether, and nextfreebuf it set to it.
-
-
- NOTE: This is used only with `attach' and `detach' commands,
- never elsewere ! No need for high speed...
- */
-
-
- /* Allocate buffer, all necessary info is taken from channel structure
- datas. If extra bytes are needed - receive buffer CRC storage for
- example - remember to request appropriate extras */
- static char *
- ddlc_buf_alloc(ddch,extra)
- struct ddlcchan *ddch;
- const int extra;
- {
- char *buf = NULL;
- int16 *blk, *blocks = (int16*) (ddch->Board->boardbase + MEMSTART),
- *memend = (int16*) (ddch->Board->boardbase + ddch->Board->boardsize);
-
- int allocsize = (ddch->bufsiz + extra);
- int16 sizflag;
-
- if (allocsize & 1) ++allocsize; /* Even number of bytes.. */
- sizflag = (unsigned)(BUF_IN_USE | allocsize);
-
-
- /* Leave when no more allocated buffer blocks are in store */
- while ((*blocks != 0) && (blocks < memend)) {
- while ((*blocks & BUF_IN_USE) && (blocks < memend)) {
- (char*)blocks += ((*blocks & ~BUF_IN_USE) + 2);
- }
- if (!*blocks) break;
- /* Some free buffer block, split it if large enough */
- if (*blocks >= (allocsize + 2 + 66)) { /* Can be split (usefully) */
- /* Mark splice place higer up in the memory */
- buf = (char*)blocks;
- blk = (int16*)(buf+2+allocsize);
- *blk = *blocks - (2 + allocsize);
- *blocks = sizflag;
- break;
- }
- if (*blocks >= allocsize) { /* Tight fit */
- buf = (char*)blocks;
- *blocks |= BUF_IN_USE;
- break;
- }
- /* Not large enough block, scan forwards.. */
- (char*)blocks += ((*blocks & ~BUF_IN_USE) + 2);
- }
- /* End of buffers..
- Pick next from nextfreebuf */
-
- /* Enlarge the heap */
- if (!buf) { /* `blocks' points to the last buffer - null+2 size */
- buf = (char*)blocks;
- *blocks = sizflag;
- /* Make sure next pointed entry is 0 size.. */
- *(int16*)(buf+2+allocsize) = 0;
- }
- if (buf > ddch->Board->nextfreebuf) {
- ddch->Board->nextfreebuf = buf + 2 + allocsize;
- ddch->Board->bufavail = (char*)memend - (buf+2+allocsize+2);
- if (ddch->Board->bufavail < 0) {
- ddch->Board->bufavail = (char*)memend - (buf+2);
- *blocks = 0;
- return NULL;
- }
- }
- return (buf+2);
- }
-
- /* Cleaning up allocated buffers for possible later re-use. */
- static void
- ddlc_buf_combiner(ddch)
- struct ddlcchan *ddch;
- {
- int16 *blocks = (int16*) (ddch->Board->boardbase + MEMSTART),
- *memend = (int16*) (ddch->Board->boardbase + ddch->Board->boardsize);
- int16 nextsiz, thissize;
-
- /* Leave when last buffer seen/combined/truncated */
- while (*blocks && (blocks < memend)) {
- /* Skip buffers in use */
- while ((BUF_IN_USE & *blocks) && (blocks < memend)) {
- (char*)blocks += (((*blocks) & ~BUF_IN_USE) +2);
- }
- if (!*blocks) break; /* Empty - that is, no buffer there */
-
- /* Now points at a free buffer */
- thissize = (*blocks) & ~BUF_IN_USE;
- if (thissize)
- nextsiz = *(int16*)((char*)blocks + thissize +2);
- else
- nextsiz = 0;
-
- while ( thissize != 0 && nextsiz != 0 &&
- !(nextsiz & BUF_IN_USE) && blocks < memend) {
- /* (Possibly) intermediate free buffers, combine */
- *blocks += (nextsiz +2);
- thissize = (*blocks) & ~BUF_IN_USE;
- if (thissize)
- nextsiz = *(int16*)((char*)blocks + thissize +2);
- else
- nextsiz = 0;
- }
- if (!(*blocks & BUF_IN_USE) && (thissize == 0 || nextsiz == 0)) {
- /* Last buffer - and free!
- Truncate the heap */
- ddch->Board->nextfreebuf = (char*)blocks;
- ddch->Board->bufavail += ((*blocks) +2);
- *blocks = 0;
- continue;
- }
- /*if (BUF_IN_USE & nextsiz)*/
- (char*)blocks += (thissize +2);
- }
- }
-
- /* Freeing buffer is simple, mark it free.
- Later run combiner to crunch them all.. */
- static void
- ddlc_buf_free(buf)
- const char *buf;
- {
- int16 *blk;
- blk = (int16*)(buf -2);
- *blk &= ~BUF_IN_USE;
- }
-
- /* Well, do a block freeup, and then combine.. */
- static void
- ddlc_buf_purge(ddch)
- struct ddlcchan *ddch;
- {
- if (ddch->rcp1)
- ddlc_buf_free(ddch->rcp1);
- ddch->rcp1 = NULL;
- if (ddch->rcp2)
- ddlc_buf_free(ddch->rcp2);
- ddch->rcp2 = NULL;
- if (ddch->txp1)
- ddlc_buf_free(ddch->txp1);
- ddch->txp1 = NULL;
- if (ddch->txp2)
- ddlc_buf_free(ddch->txp2);
- ddch->txp2 = NULL;
- ddlc_buf_combiner(ddch);
- }
-
-
-
-
-
- /* INTERRUPT: Classified as transmit interrupt,
- queue more material for transmit, or whatever.. */
- static void
- ddlc_txint(ddev, channel, data_ok)
- DDLC far *ddev;
- const int channel;
- const int data_ok;
- {
- /* Material in the send queue and Tx is idle, or the DDLC-
- chip Tx-unit gave an interrupt for some reason (Xmit done?) */
- struct ddlcchan * ddch;
- CH_REGS *chregs;
- int16 len;
-
- ddch = & Ddlcchan[channel];
- chregs = & ddev->Ch_Regs[channel & 1];
-
- /* If data_ok is not negative, this is an acknowledge
- of sent frame, get more from queue, if there is anything
- to be sent.. */
-
- if (data_ok >= 0)
- ddch->txints++;
- /* XX: Process Tx interrupt, and queue a datagram.. */
- /* Frame complete vs. DMA complete ? */
-
- if (data_ok >= 0)
- ddch->txside = !ddch->txside;
-
- /* XX: Transmit routines... all fun tricks for sync operations.. */
- /* XX: Delays for half-duplex ??? */
-
- /* Queue next packet for transmit, and initiate it */
- if (ddch->txside == 0) {
- /* Prepare buffer, and initiate it for transfer */
- len = len_p(ddch->sndq);
- pullup((void*)ddch->sndq,(void*)ddch->txp1,len);
- cputw(&chregs->TxBase, ddch->TxAbase);
- cputw(&chregs->TxFrLen, len);
- chregs->TxCont.tx_control.BR = 1;
- chregs->TxStat.tx_stat.TDC = 0; /* Either TDC or TFC, */
- chregs->TxStat.tx_stat.TFC = 0; /* zero both.. */
- } else {
- /* Prepare buffer, and initiate it for transfer */
- len = len_p(ddch->sndq);
- pullup((void*)ddch->sndq,(void*)ddch->txp2,len);
- cputw(&chregs->TxBase, ddch->TxBbase);
- cputw(&chregs->TxFrLen, len);
- chregs->TxCont.tx_control.BR = 1;
- chregs->TxStat.tx_stat.TDC = 0; /* Either TDC or TFC, */
- chregs->TxStat.tx_stat.TFC = 0; /* zero both.. */
- }
- /* XX: Any more of these fancy things -- like TxDELAY, etc features ? */
- }
-
- static void
- ddlc_txintdefer(ddch)
- struct ddlcchan *ddch;
- {
- int channel = ddch->dev;
- int subchannel = ddch->unit;
- DDLC *ddev = &((DDLC*)ddch->Board->boardbase)[subchannel >> 1];
-
- ddlc_txint(ddev,channel,-1);
- }
-
-
-
- /* INTERRUPT: Classified as receiver interrupt, handle incoming
- packet. */
- static void
- ddlc_rxint(ddev, channel, data_ok)
- DDLC far *ddev;
- const int channel;
- const int data_ok;
- {
- struct ddlcchan * ddch = &Ddlcchan[channel];
- CH_REGS *ddevch = & ddev->Ch_Regs[channel & 1];
- int16 framesize;
- struct mbuf *buf;
-
- /* Process incoming frame(s) */
-
- DISABLE(); /* ?? really necessary ? */
-
- ddch->rxints++;
-
- /* If present rxside is 0, activate buffer B, then continue with
- handling the data in block A. Else A and B :) */
- if (ddch->rxside == 0) {
- /* Buffer B to be activated */
- ddevch->RxCont.rx_control.BBR = 1;
- ddevch->RxCont.rx_control.BBR = 0;
- } else {
- /* Buffer A to be activated */
- ddevch->RxCont.rx_control.BAR = 1;
- ddevch->RxCont.rx_control.BAR = 0;
- }
-
- /* Do both (A and B buffer) checks, alter order depending on
- what is the value of the side indicator (0 or 1) */
-
- if ((ddch->rxside == 0) &&
- ddevch->RxStat.rx_status.RAC /* Buf A receive complete */) {
- if (data_ok) {
- /* received byte count includes 2 CRC bytes */
- framesize = cgetw(&ddevch->RxAByteCnt) - 2;
- buf = alloc_mbuf(framesize + sizeof(struct iface *));
- memcpy(buf->data+sizeof(struct iface *),
- ddch->rcp1,
- framesize);
- buf->cnt = framesize;
- net_route(ddch->iface, buf);
- ddch->rxframes++;
- }
- ddevch->RxStat.rx_status.RAC = 0;
- }
- if ((ddch->rxside != 0) &&
- ddevch->RxStat.rx_status.RBC /* Buf B receive complete */) {
- if (data_ok) {
- /* received byte count includes 2 CRC bytes */
- framesize = cgetw(&ddevch->RxBByteCnt) - 2;
- buf = alloc_mbuf(framesize + sizeof(struct iface *));
- memcpy(buf->data+sizeof(struct iface *),
- ddch->rcp2,
- framesize);
- buf->cnt = framesize;
- net_route(ddch->iface, buf);
- ddch->rxframes++;
- }
- ddevch->RxStat.rx_status.RBC = 0;
- }
-
- ddch->rxside = !ddch->rxside; /* Toggle side indicator value */
- RESTORE(); /* ?? Really necessary ? */
- }
-
-
- /* ---------------------------------------------------------------- */
-
- /* ddlc_init() -- initialize board for channel attach operations. */
-
-
- static int
- ddlc_init(boardnum, boardbase, ivec, memsize)
- const int boardnum;
- char far *boardbase;
- const int ivec;
- unsigned int memsize;
- {
- DDLC *ddev = (DDLC*)boardbase;
- int initcnt = 2;
- int nchips = 0; /* Assume it has 2 chips, trust to find only 1.. */
- int timecount, ok;
- int cfgval;
-
- /* Disable all operations of the board, enable proper
- IRQs for operation, etc.. */
-
- ok = 1;
- while(initcnt--) {
- /* Do SW RESET of the chip */
- timecount = 2000; /* Max time should be 512 clock cycles
- at 14.xx MHz... Under normal conditions
- this counter does not have time to
- count at all! */
- ddev->System.sys_control.RESET = 1;
- while (ddev->System.sys_control.RESET && --timecount)
- ; /* Wait it to finish */
- if (timecount == 0) ok = 0;
-
- /* Test some reset clearable register location - at first
- by storing there a bit-pattern and then reading it back,
- issue RESET, look again. If fails, then claim an error */
-
- ddev->Ch_Regs[0].AddCmp.AddrComp0 = 0xAA;
- if (ddev->Ch_Regs[0].AddCmp.AddrComp0 != 0xAA) ok = 0;
-
- ddev->Ch_Regs[0].AddCmp.AddrComp0 = 0x55;
- if (ddev->Ch_Regs[0].AddCmp.AddrComp0 != 0x55) ok = 0;
-
- ddev->System.sys_control.RESET = 1;
- while (ddev->System.sys_control.RESET && --timecount)
- ; /* Wait it to finish */
- if (timecount == 0) ok = 0;
-
- if (ddev->Ch_Regs[0].AddCmp.AddrComp0 != 0) ok = 0;
-
- if (!ok)
- break;
- /* No DDLC chip found.. */
-
- /* Did set the chip into a known state.. */
-
- /* Configure serial channels into Packet-Modem -style
- interfaces */
- ddev->Ch_Regs[0].SerControl.serial_control.MODE = 0x8;
- ddev->Ch_Regs[1].SerControl.serial_control.MODE = 0x8;
-
- /* Now enable proper Interrupts ..*/
- ddev->Interrupt.IntEn = 0; /* First disable them all.. */
- /* Enable needed ones with channel attaches */
-
- ++ddev; /* Next device if nchips is 2.. */
- ++nchips;
- }
-
- if (nchips == 0) {
- printf("DDLC chip not found at 0x%05lX. \"attach ddlc init\" failed!\n",
- (((long)FP_SEG(boardbase))<<4)+(long)FP_OFF(boardbase));
- return -1;
- }
-
-
- cfgval = 03 & *((char*)boardbase + CONFIGREG);
-
- if (cfgval == 3 && memsize == 0) {
- printf("DDLC: memsize param missing; No config read register on this board version.\n");
- return -1;
- }
-
- if (memsize == 0) {
- /* Sniff the size of the buffer memory */
- if (1 & cfgval)
- memsize = 32*1024;
- else
- memsize = 16*1024;
- }
-
- /* Reset (zero) the buffer memory */
- memset(boardbase+MEMSTART,0,memsize-MEMSTART);
-
-
- Ddlc[boardnum].channelcnt = nchips << 1; /* *2 */ /* XX: LOG_CHANNELS_ON_BOARD ? */
- Ddlc[boardnum].vec = ivec;
- Ddlc[boardnum].ints = 0L;
- Ddlc[boardnum].dtrreg = 0xFF; /* All lines high.. */
- Ddlc[boardnum].boardbase = boardbase;
- Ddlc[boardnum].boardsize = memsize;
- Ddlc[boardnum].oldvec = getirq(ivec);
- Ddlc[boardnum].nextfreebuf = boardbase + MEMSTART; /* SRAM start */
- Ddlc[boardnum].bufavail = memsize - MEMSTART;
-
- *(boardbase+DTRREG) = Ddlc[boardnum].dtrreg;
-
- DISABLE();
- maskon(ivec);
- RESTORE();
-
- setirq(ivec,Ddlc_handle[boardnum]);
- return 0;
- }
-
- /*
- * Attach a DDLC interface to the system
- * argv[0]: hardware type, must be "ddlc"
- * argv[1]: "init" - a special case telling about boards
- * Max insertation is DDLCMAX -boards (2 by default)
- * argv[2]: io-base-address (memory-mapped board)
- * argv[3]: IRQ vector number
- * argv[4]: (OPTIONAL!) memory size -- 16kB/32kB (first char is checked)
- * else:
- * argv[1]: channel nbr: 0..3 in the first board, 4..7 in the next, etc.
- * argv[2]: mode, one of: "ax25", "raw", "hdlc"
- * argv[3]: interface label, e.g., "dd0"
- * argv[4]: maximum transmission unit, bytes (MTU)
- * argv[5]: Informative guess of interface speed (9600/56000/64000/1E6/...)
- * this is listed in status, but does not affect links.
- * argv[6]: Duplex-type: "f" or "h" (by first char)
- * argv[7]: Interface IP address, optional (defaults to Ip_addr);
- */
- int
- ddlc_attach(argc,argv)
- int argc;
- char *argv[];
- {
- #ifdef AX25
- int rawencoding = 0;
- #endif
- int channel, board, mtu, duplex, rc;
- struct iface *if_h;
- struct ddlcchan *ddch;
- DDLC *ddev;
- CH_REGS *ddevch;
- int32 addr1 = 0L;
- char *baseaddr, *cp;
-
-
- if (strcmp(argv[1],"init")==0) {
- int vecnum;
- int32 baseaddr;
- unsigned int memsize = 0;
-
-
- if (argc < 4/*3+1*/ || argc > 5/*4+1*/) {
- printf("Bad attach ddlc init -command. Wrong arg count.\n");
- return -1;
- }
- if (boardcnt >= DDLCMAX) {
- printf("Too many DDLC adapters\n");
- return -1;
- }
- board = boardcnt++;
- baseaddr = htol(argv[2]);
- if ((baseaddr < 0xA0000) || (baseaddr >= 0xFC000)) {
- printf("Adapter base address is bad. Must be in range 0xA0000 - 0xFC000\n"
- " You gave: `%s'\n",argv[2]);
- return -1;
- }
- baseaddr <<= 12; /* XX: Very PC-specific, but so is the card too..*/
- vecnum = atoi(argv[3]);
- if (vecnum < 2 || vecnum > 7) {
- printf("INT vector (`%s') bad for the DDLC board. Must be in range 2..7\n",argv[4]);
- return -1;
- }
- if (argc == 5) {
- if (*argv[4] != '1' && *argv[4] != '3') {
- printf("optional MEMSIZE argument is bad: `%s' If given, must be 16kB or 32kB\n",argv[4]);
- }
- memsize = 32*1024;
- if (*argv[4] == '1')
- memsize = 16*1024;
- }
-
- rc = ddlc_init( board, (char*)baseaddr, vecnum, memsize);
- if (rc == -1) --boardcnt;
- return rc;
- }
-
- if (argc < 7 || argc > 8+1) {
- printf("too few or too many arguments\n");
- return -1;
- }
- channel = atoi(argv[1]);
- board = channel >> LOG_CHANNELS_ON_BOARD;
- if (channel < 0 || board >= boardcnt) {
- printf("No attached card for given channel %d\n",channel);
- return -1;
- }
- /* XX: CHANNELS_ON_BOARD ? */
- if (Ddlc[board].channelcnt == 2 &&
- (channel & 3) > 1) {
- printf("only 2 channels on this card (board:%d)\n",board);
- return -1;
- }
- if (Ddlc[board].attachlog & (1 << (channel & (CHANNELS_ON_BOARD - 1)))) {
- printf("channel %d has already been attached.\n",channel);
- return -1;
- }
- #ifdef AX25
- if (stricmp(argv[2],"ax25")==0) {
- rawencoding = 0;
- if (Mycall[0] == '\0') {
- printf("set mycall for AX25 first\n");
- return -1;
- }
- } else
- #endif
- if (stricmp(argv[2],"raw")==0 ||
- stricmp(argv[2],"hdlc")==0)
- #ifdef AX25
- rawencoding = 1;
- #else
- ;
- #endif
- else {
- printf("Bad encapsulation format: `%s'\n "
- #ifdef AX25
- "AX25, and "
- #endif
- "HDLC (alias RAW) supported\n",
- argv[2]);
- return -1;
- }
- if (if_lookup(argv[3]) != NULLIF) {
- printf("Interface %s already exists\n",argv[3]);
- return -1;
- }
- if ((mtu = atoi(argv[4])) < 64 || mtu > 4095) {
- printf("MTU value `%s' invalid\n",argv[4]);
- return -1;
- }
- if (*argv[6] != 'f' && *argv[6] != 'h') {
- printf("Duplex value `%s' is bad. Use `half' or `full'\n",argv[6]);
- return -1;
- }
- duplex = (*argv[6] == 'f');
- addr1 = Ip_addr;
- if (argc > 7)
- addr1 = resolve(argv[7]);
- if (addr1 == 0L) {
- printf(Noipaddr);
- return -1;
- }
-
- /* Create new interface structure */
- if_h = (struct iface *) callocw(1,sizeof(struct iface));
- if_h->addr = addr1;
- if_h->name = strdup(argv[3]);
- if_h->mtu = mtu;
- if_h->ioctl = ddlc_ctl;
- if_h->dev = channel;
- if_h->stop = ddlc_stop;
- if_h->raw = ddlc_raw;
- ddch = &Ddlcchan[channel];
- ddch->iface = if_h;
- if (ddch->speed) free(ddch->speed);
- ddch->speed = strdup(argv[5]);
- ddch->Board = &Ddlc[board];
-
- ddch->bufsiz = mtu;
- ddch->rcp1 = ddlc_buf_alloc(ddch,2);
- ddch->rcp2 = ddlc_buf_alloc(ddch,2);
- ddch->txp1 = ddlc_buf_alloc(ddch,0);
- ddch->txp2 = ddlc_buf_alloc(ddch,0);
- ddch->rxside = 0;
- ddch->txside = 0;
-
- if (!ddch->txp2) {
- /* Buffer allocation failure, free all allocated data.. */
- printf("Out of interface buffer memory on channel `%s'\n",
- if_h->name);
- ddlc_buf_purge(ddch);
- free(ddch->speed);
- ddch->speed = NULL;
- free(if_h->name);
- free(if_h);
- return -1;
- }
-
- /* Init DDLC frame registers */
-
- ddev = (DDLC *)(ddch->Board->boardbase);
- if (2 & channel) ++ddev; /* on the 2nd chip of the board */
- ddevch = & ddev->Ch_Regs[channel & 1];
- cputw(&ddevch->RxBufLen,mtu);
- cputw(&ddevch->TxFrLen, 0);
- baseaddr = ddch->Board->boardbase;
- cputw(&ddevch->RxABase, (int16)(ddch->rcp1 - baseaddr));
- cputw(&ddevch->RxBBase, (int16)(ddch->rcp2 - baseaddr));
- ddch->TxAbase = ddch->txp1 - baseaddr;
- ddch->TxBbase = ddch->txp2 - baseaddr;
-
- /* Store other operative data */
-
- ddch->dev = channel;
- ddch->unit = channel % 4;
- ddch->duplex = duplex;
- ddch->tstate = IDLE;
- ddch->rstate = ACTIVE;
- /* default channel access Params */
- ddch->txdelay = 5; /* 50 ms */
- ddch->persist = 128; /* 50% persistance */
- ddch->slotime = 30; /* 300 ms */
- ddch->squeldelay = 3; /* 30 ms */
-
- #ifdef AX25
- if (rawencoding)
- setencap(if_h,"HDLC");
- else {
- setencap(if_h,"AX25");
- if (if_h->hwaddr == NULLCHAR)
- if_h->hwaddr = mallocw(AXALEN);
- memcpy(if_h->hwaddr,Mycall,AXALEN);
- }
- #else
- setencap(if_h,"HDLC"); /* No choise - only raw HDLC framing */
- #endif
-
- /* Link the interface into the interface list */
- if_h->next = Ifaces;
- Ifaces = if_h;
-
- /* Initialize the defer timer */
- ddch->defer.func = (void *)ddlc_txintdefer;
- ddch->defer.arg = ddch;
- set_timer(&ddch->defer,MSPTICK);
-
- cp = if_name(if_h, " tx");
- if_h->txproc = newproc(cp,512,if_tx,0,if_h,NULL,0);
- free(cp);
-
- #define DINT ddev->Interrupt.int_enable
- if ((channel & 1) == 0) {
- /* Chip channel 0, Interrupt enables ON */
- /* If duplex channel, use DMA Complete interrupts to
- indicate frame completition. Then queue next frame
- to be sent... For radio-channels, use Frame Complete
- interrupt which tells when sent frame is finished from
- transmitter */
- if (ddch->duplex)
- DINT.Tx0DMAC = 1;
- else
- DINT.Tx0FC = 1;
- DINT.Rx0DMAC = 1;
- DINT.Rx0Idl = 1;
- DINT.CD0 = 1;
- } else {
- /* Chip channel 1, Interrupt enables ON */
- if (ddch->duplex)
- DINT.Tx1DMAC = 1;
- else
- DINT.Tx1FC = 1;
- DINT.Rx1DMAC = 1;
- DINT.Rx1Idl = 1;
- DINT.CD1 = 1;
- }
- cputw(&ddevch->RxCont.RxCntr, 0);
- ddevch->RxCont.rx_control.BAR = 1; /* Start with buffer A active */
- cputw(&ddevch->RxCont.RxCntr, 0);
- cputw(&ddevch->TxCont.TxCntr, 0);
- #undef DINT
-
- Ddlc[board].attachlog |= 1 << (channel & 3); /* Mark up that this has been attached */
- return 0;
- }
-
- /* SET Transmit or Receive Mode
- * Set RTS (request-to-send) to modem on Transmit
- */
- static void
- ddlc_rts(ddch, x)
- register struct ddlcchan *ddch;
- int16 x;
- {
- int unit = ddch->unit;
- int chan = unit & 1;
- DDLC *ddev = (DDLC*)(ddch->Board->boardbase)[unit >> 1];
-
- /* Turn on the transmitter to send flags */
- if(x == ON){ /* Turn Tx ON */
- if (ddch->duplex == 0) { /* Half-duplex.. */
- /* .. also turn the receiver OFF */
- ddev->Ch_Regs[chan].RxCont.rx_control.RE = 0;
- ddch->rstate = IDLE;
- }
- ddev->Ch_Regs[chan].TxCont.tx_control.TE = 1;
- /* Transmitter now on */
- } else { /* Tx OFF and Rx ON */
- ddch->tstate = IDLE;
- ddch->rstate = ACTIVE; /* Normal state */
- ddev->Ch_Regs[chan].RxCont.rx_control.RE = 1;
- ddev->Ch_Regs[chan].TxCont.tx_control.TE = 0;
-
- /* setup_ddlc_rx_dma(ddch); */
- /* Hold tx off long enough for other station to reply */
- ddch->deftime = msclock() + ddch->txdelay + 100; /* XX: values?? */
- }
- return;
- }
-
-
- /* Subroutine to set KISS params in channel tables */
- static int32
- ddlc_ctl(iface, cmd, set, val)
- struct iface *iface;
- int cmd;
- int set;
- int32 val;
- {
- struct ddlcchan *ddch;
- int32 t,ca;
- int16 i;
- DDLC *ddev;
- CH_REGS *ddevch;
- int16 channel, unit;
-
- channel = iface->dev;
- ddch = &Ddlcchan[ channel ]; /* point to channel table */
- unit = ddch->unit;
- ddev = (DDLC*)ddch->Board->boardbase;
- ddevch = & ddev->Ch_Regs[unit & 1];
- if (unit & 2)
- ++ddevch;
-
- switch(cmd){
- case PARAM_DATA: /* XX: ?? */
- break;
- case PARAM_TXDELAY:
- if(set)
- ddch->txdelay = val;
- return ddch->txdelay;
- case PARAM_PERSIST:
- if(set)
- ddch->persist = val;
- return uchar(ddch->persist);
- case PARAM_SLOTTIME:
- if(set)
- ddch->slotime = val;
- return ddch->slotime;
- case PARAM_TXTAIL:
- if(set)
- ddch->squeldelay = val;
- return ddch->squeldelay;
- case PARAM_FULLDUP:
- if (set)
- if (ddch->duplex != val) {
- /* XX: Alter interrupt register usage! */
- ddch->duplex = val;
- }
- return ddch->duplex;
- case PARAM_HW: /* XX: ?? */
- break;
- case PARAM_MUTE:
- if(set){
- if(val == -1){
- /* Special case for duration of a CTS */
- val = ddch->txdelay + 500;
- }
- ddch->deftime = msclock() + val;
- }
- t = msclock();
- ca = ddch->deftime - t;
- if(ca < 0){
- ddch->deftime = t;
- ca = 0;
- }
- return ca;
- case PARAM_DTR: /* DTR value setup, REMEMBER: LOW ACTIVE */
- i = 1 << (4 + unit);
- if (set) {
- if (val==0)
- ddch->Board->dtrreg |= i;
- else
- ddch->Board->dtrreg &= ~i;
- *((unsigned char*)(ddch->Board->boardbase) + DTRREG) =
- ddch->Board->dtrreg;
- }
- return !(i & ddch->Board->dtrreg);
- case PARAM_RTS: /* XX: ?? */
- if (set) {
- DISABLE(); /* ?? Taken from those previous models..
- This chip might not need these */
- ddevch->TxCont.tx_control.TE = val;
- RESTORE();
- }
- return ddevch->TxCont.tx_control.TE;
- case PARAM_SPEED: /* XX: ?? */
- break;
- case PARAM_ENDDELAY: /* XX: ?? */
- if (set)
- ddch->enddelay = val;
- return ddch->enddelay;
- case PARAM_GROUP: /* XX: ?? */
- if (set)
- ddch->group = val;
- return ddch->group;
- case PARAM_IDLE: /* XX: ?? */
- if (set)
- ddch->idletime = val;
- return ddch->idletime;
- case PARAM_MIN: /* XX: ?? */
- if (set)
- ddch->mintime = val;
- return ddch->mintime;
- case PARAM_MAXKEY: /* XX: ?? */
- if (set)
- ddch->maxkeyup = val;
- return ddch->maxkeyup;
- case PARAM_WAIT: /* XX: ?? */
- if (set)
- ddch->waittime = val;
- return ddch->waittime;
- case PARAM_DOWN: /* XX: ?? */
- break;
- case PARAM_UP: /* XX: ?? */
- break;
- case PARAM_BLIND: /* XX: ?? */
- break;
- case PARAM_RETURN: /* XX: ?? */
- break;
- default:
- break;
- }
- return -1;
- }
-
-
- static int
- ddlc_stop(iface)
- struct iface *iface;
- {
- int channel = iface->dev;
- struct ddlcchan *ddch = &Ddlcchan[channel];
- int chipchannel = 1 & channel;
- DDLC *ddev = &((DDLC*)(ddch->Board->boardbase))[1 & (channel >> 1)];
-
- if (!(ddch->Board->attachlog & (1 << (channel & 3)))) {
- return -1;
- }
- DISABLE();
-
- /* Disable interrupts related to specified channel */
- #define DINT ddev->Interrupt.int_enable
- if (chipchannel == 0) {
- /* Chip channel 0, Interrupt enables off */
- DINT.Tx0FC = 0;
- DINT.Tx0DMAC = 0;
- DINT.Rx0DMAC = 0;
- DINT.Rx0Idl = 0;
- DINT.CD0 = 0;
- } else {
- /* Chip channel 1, Interrupt enables off */
- DINT.Tx1FC = 0;
- DINT.Tx1DMAC = 0;
- DINT.Rx1DMAC = 0;
- DINT.Rx1Idl = 0;
- DINT.CD1 = 0;
- }
- cputw(&ddev->Ch_Regs[chipchannel].RxCont.RxCntr, 0);
- cputw(&ddev->Ch_Regs[chipchannel].TxCont.TxCntr, 0);
- #undef DINT
-
- RESTORE();
- ddlc_buf_purge(ddch);
-
- ddch->Board->attachlog &= ~(1 << (channel & 3));
-
- /* XX: Do necessary DDLC shutdown things on ONE channel */
- /* XX: If no more channels on that board, detach the board ??? */
- #if 0
- DISABLE();
- /* mask off interrupt input */
- maskoff(ddch->vec);
-
- /* restore original interrupt vector */
- setirq(ddch->vec, ddch->oldvec);
- RESTORE();
- #endif
- return 0;
- }
-
- int
- ddlcstop()
- {
- return 0;
- }
-
-
- /* Send raw packet on DDLC interface */
- static int
- ddlc_raw(iface,bp)
- struct iface *iface;
- struct mbuf *bp;
- {
- struct ddlcchan *ddch = &Ddlcchan[iface->dev];
-
- enqueue(&ddch->sndq, bp);
-
- /* See if anything is being transmitted */
- if (ddch->tstate == IDLE)
- ddlc_txintdefer(ddch);
- return 0;
- }
-
-
- /*
- static void
- setup_ddlc_rx_dma(ddch)
- struct ddlcchan *ddch;
- {
- }
-
- static void
- setup_ddlc_tx_dma(ddch, buffer, length)
- struct ddlcchan *ddch;
- char *buffer;
- int length;
- {
- }
- */
-
- static void
- tdelay(ddch, time)
- register struct ddlcchan *ddch;
- unsigned int time;
- {
- }
-
-
- /* XX: Hanged into generic timer queue for whatever use.. */
- int
- ddlctimer()
- {
- return 0;
- }
-
-
-
- static int
- buf_ptr_name(s,board,buf)
- char **s;
- const int board;
- char far *buf;
- {
- int i;
- struct ddlcchan *ddch = &Ddlcchan[ board << 2 ];
-
- buf += 2; /* Adjust it to be to a buffer block */
-
- for (i=0; i<4; ++i) {
- if (!ddch->iface) continue; /* Not attached */
- if ((ddch->rcp1) == buf) { *s = "Rcp1"; return ddch->dev; }
- if ((ddch->rcp2) == buf) { *s = "Rcp2"; return ddch->dev; }
- if ((ddch->txp1) == buf) { *s = "Txp1"; return ddch->dev; }
- if ((ddch->txp2) == buf) { *s = "Txp2"; return ddch->dev; }
- ++ddch;
- }
- *s = "Unknown";
- return -1;
- }
-
-
- /* Various feature commands for this card:
- ddlc stat <board>
- ...
- */
- static int doddlc_stat __ARGS((const int, const char **));
- static int
- doddlc_stat(argc,argv)
- const int argc;
- const char *argv[];
- {
- volatile int16 *buf, *memend;
- int i, board = -1;
- int lastchan;
- struct ddlcchan *ddch;
- struct DDLCTAB *Board = NULL;
-
- board = atoi(argv[2]);
- if (board < 0 ||
- board >= boardcnt) {
- printf("No such DDLC board `%s'\n",argv[2]);
- return -1;
- }
- Board = &Ddlc[board];
-
- buf = (int16*)(Board->boardbase + MEMSTART);
- memend = (int16*)(Board->boardbase + Board->boardsize);
- printf("Board:\n");
- printf(" base addr = %04X:%04X",FP_SEG(Board->boardbase),
- FP_OFF(Board->boardbase));
- printf(" IRQ = %d",Board->vec);
- printf(" Channel count = %d",Board->channelcnt);
- printf(" Attached channels: ");
- for (i=0;i<4;++i) {
- if ((1 << i) & Board->attachlog)
- printf("%d ",i);
- }
- if (Board->attachlog == 0)
- printf("none");
-
- printf("\n");
- printf(" memory size = %ld (0x%04lX)",
- (unsigned long)Board->boardsize,
- (unsigned long)Board->boardsize);
- printf(" bufavail = %ld",(unsigned long)Board->bufavail);
- printf(" nextfreebuf = 0x%04X (%d)\n",
- FP_OFF(Board->nextfreebuf),
- FP_OFF(Board->nextfreebuf));
- printf("Memory buffers:\n");
- if (*buf) {
- do {
- int16 val = *buf;
- int chan;
- char *s;
- printf( " 0x%04X: %5d bytes - %s ",
- FP_OFF(buf), val & ~BUF_IN_USE,
- BUF_IN_USE & val ? "in use":"free ");
- if (BUF_IN_USE & val) {
- chan = buf_ptr_name(&s,board,buf);
- printf(" owner: chan %d %s",chan,s);
- }
- printf("\n");
- fflush(stdout);
- pwait(NULL);
- (char*)buf += ((val & ~BUF_IN_USE) +2);
- } while (*buf && (buf < memend));
- } else
- printf(" None in use!\n");
-
- if (*buf)
- printf( " 0x%04X: %5d bytes - %s -- outside memory!\n",
- FP_OFF(buf), *buf & ~BUF_IN_USE,
- BUF_IN_USE & *buf ? "in use":"free"),
- fflush(stdout),
- pwait(NULL);
- lastchan = (board+1) << LOG_CHANNELS_ON_BOARD;
- for (i=board << LOG_CHANNELS_ON_BOARD;i<lastchan;++i) {
- ddch = &Ddlcchan[ i ];
-
- printf("Channel: %d ",i);
- if (Board->channelcnt <= (i & (CHANNELS_ON_BOARD-1))) {
- printf("NOT PRESENT\n");
- continue;
- }
- if (Board->attachlog & (1 << ((CHANNELS_ON_BOARD-1) & i))) {
- printf("attached as `%s'\n",ddch->iface->name);
- } else {
- printf("not attached\n");
- }
- printf(" rxints: %4ld", ddch->rxints);
- printf(" txints: %4ld", ddch->txints);
- printf(" exints: %4ld", ddch->exints);
- printf(" enqueued: %4ld\n",ddch->enqueued);
- printf(" rxframes: %4ld", ddch->rxframes);
- printf(" crcerrs: %4d", ddch->crcerrs);
- printf(" rovers: %4d", ddch->rovers);
- printf(" tunders: %4d", ddch->tunders);
- printf(" bufsiz: %4u\n", ddch->bufsiz);
- printf(" RxAbase: 0x%04X", ddch->rcp1 - Board->boardbase);
- printf(" RxBbase: 0x%04X", ddch->rcp2 - Board->boardbase);
- printf(" TxAbase: 0x%04X", ddch->TxAbase);
- printf(" TxBbase: 0x%04X\n",ddch->TxBbase);
- fflush(stdout);
- pwait(NULL);
- }
- return 0;
- }
- static int doddlc_test __ARGS((void));
- static int
- doddlc_test()
- {
- #define TESTSIZ 10240
- char *s = (char*)malloc(TESTSIZ);
- char *b = Ddlc[0].boardbase + MEMSTART; /* Buffer start */
- if (boardcnt < 1) {
- printf("Sorry, board 0 has not been initialized! Can't test!\n");
- return -1;
- }
-
- printf("Running buffer data movement tests, 838 ns/clock count. Overflow at 55ms.\n");
- swstart();
- memcpy(s,b,TESTSIZ);
- swstop(0);
- printf("memcpy(ddlcbuf->coremem,%d), watch chan 0\n",TESTSIZ);
-
- swstart();
- memcpy(b,s,TESTSIZ);
- swstop(1);
- printf("memcpy(ddlcbuf<-coremem,%d), watch chan 1\n",TESTSIZ);
-
- free(s);
- return 0;
- }
-
- /* Some misc test/statistics commands from primary command "ddlc" */
-
- int
- do_ddlc(argc,argv)
- int argc;
- char *argv[];
- {
- int i, ok = 1;
- enum {Cmd_None = -1, Cmd_Stat, Cmd_Test } cmd;
-
-
- cmd = Cmd_None;
- if (argc < 2)
- ok = 0;
-
- if (ok && stricmp(argv[1],"stat")==0 && argc == 3)
- cmd = Cmd_Stat;
- else if (ok && stricmp(argv[1],"test")==0 && argc == 2)
- cmd = Cmd_Test;
-
- if (!ok || cmd == Cmd_None) {
- printf("Usage:\tddlc stat <board>\n\tddlc test\n");
- return -1;
- }
- switch (cmd) {
- case Cmd_Stat:
- return doddlc_stat(argc,argv);
- case Cmd_Test:
- return doddlc_test();
- default:
- ;
- }
- printf("DDLC sub-command `%s' not implemented!\n",argv[1]);
- return -1;
- }
-